package com.onlyxiahui.framework.net.handler;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.onlyxiahui.framework.net.handler.connect.ConnectHandler;
import com.onlyxiahui.framework.net.handler.connect.Connector;
import com.onlyxiahui.framework.net.handler.connect.action.ConnectBackAction;
import com.onlyxiahui.framework.net.handler.connect.action.ConnectStatusAction;
import com.onlyxiahui.framework.net.handler.connect.data.ConnectData;
import com.onlyxiahui.framework.net.handler.connect.type.IdleStatus;
import com.onlyxiahui.framework.net.handler.data.DataHandler;
import com.onlyxiahui.framework.net.handler.data.action.DataBackAction;
import com.onlyxiahui.framework.net.handler.data.action.DataMonitorRead;
import com.onlyxiahui.framework.net.handler.data.action.DataMonitorWrite;
import com.onlyxiahui.framework.net.handler.data.action.DataSendAction;
import com.onlyxiahui.framework.net.handler.data.handler.MessageHandler;
import com.onlyxiahui.framework.net.handler.data.handler.ReceiveHandler;
import com.onlyxiahui.framework.net.handler.thread.HandlerThreadPoolBuilder;

/**
 * Description <br>
 * Date 2020-05-12 17:19:58<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class NetHandler {
	protected final Logger logger = LoggerFactory.getLogger(this.getClass());
	private final ExecutorService pool = HandlerThreadPoolBuilder.build();
	private boolean run = true;
	private long sleepTimestamp = 10;

	private Set<DataMonitorRead> monitorReadSet = new HashSet<>();
	private Set<DataMonitorWrite> monitorWriteSet = new HashSet<>();

	ConnectHandler connectHandler;
	DataHandler dataHandler;
	MessageHandler messageHandler = new MessageHandler() {

		@Override
		public void receive(Object data) {
			monitorRead(data);
			dataHandler.receive(data);
		}

		@Override
		public void exceptionData(String key) {
			dataHandler.addExceptionData(key);
		}
	};

	public NetHandler() {
		initServer();
		initAction();
		initThread();
	}

	public NetHandler(Connector connector) {
		initServer();
		initAction();
		initThread();
		this.setConnector(connector);
	}

	private void initServer() {
		// 数据发送线程
		connectHandler = new ConnectHandler();
		dataHandler = new DataHandler();
	}

	private void initAction() {
		/*** 为处理连接的线程添加连接状态变化的action，当连接断开了或者连接成功触发 ***/
		connectHandler.addConnectStatusAction(new ConnectStatusAction() {

			@Override
			public void onStatusChange(boolean isConnected) {
				// 当连接断开了，将已发送信息标记为失败，让处理读信息的线程处理
			}

			@Override
			public void onAfterAutoConnect(boolean connected) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onIdle(IdleStatus idleStatus) {
				// TODO Auto-generated method stub
			}
		});
		dataHandler.addDataSendAction(new DataSendAction() {

			@Override
			public boolean send(Object data) {
				boolean send = false;
				if (connectHandler.isConnected()) {
					try {
						connectHandler.getConnector().write(data);
						send = true;
					} catch (Exception e) {
						logger.error("", e);
					}
				}
				return send;
			}
		});
		dataHandler.addDataSendAction(new DataSendAction() {

			@Override
			public boolean send(Object data) {
				if (connectHandler.isConnected()) {
					monitorWrite(data);
				}
				return true;
			}
		});
		dataHandler.setLostStatus(() -> !connectHandler.isConnected());
	}

	private void initThread() {
		pool.execute(new Runnable() {

			@Override
			public void run() {
				while (run) {
					connectHandler.handle();
					sleep();
				}
			}

			public void sleep() {
				try {
					Thread.sleep(sleepTimestamp);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		pool.execute(new Runnable() {

			@Override
			public void run() {
				while (run) {
					dataHandler.handle();
					sleep();
				}
			}

			public void sleep() {
				try {
					Thread.sleep(sleepTimestamp);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}

	private void initConnector(Connector connector) {
		if (null != connector) {
			connector.addMessageHandler(messageHandler);
			connectHandler.setConnector(connector);
		}
	}

	public long getSleepTimestamp() {
		return sleepTimestamp;
	}

	public void setSleepTimestamp(long sleepTimestamp) {
		this.sleepTimestamp = sleepTimestamp;
	}

	public void setConnector(Connector connector) {
		initConnector(connector);
	}

	public Connector getConnector() {
		return connectHandler.getConnector();
	}

	public ConnectHandler getConnectHandler() {
		return connectHandler;
	}

	public DataHandler getDataHandler() {
		return dataHandler;
	}

	public void addReceiveHandler(ReceiveHandler handler) {
		dataHandler.addReceiveHandler(handler);
	}

	public void connect(ConnectData connectData, ConnectBackAction action) {
		connectHandler.connect(connectData, action);
	}

	public boolean addConnectStatusAction(ConnectStatusAction action) {
		return connectHandler.addConnectStatusAction(action);
	}

	public boolean isAutoConnect() {
		return connectHandler.isAutoConnect();
	}

	public void send(Object data) {
		getDataHandler().send(null, data, null);
	}

	public void send(String key, Object data) {
		getDataHandler().send(key, data, null);
	}

	public void send(String key, Object data, DataBackAction dataBackAction) {
		getDataHandler().send(key, data, dataBackAction);
	}

	public void closeConnect() {
		getConnectHandler().closeConnect();
	}

	public void stop() {
		this.run = false;
		this.closeConnect();
	}

	private void monitorRead(Object data) {

		try {
			for (DataMonitorRead m : monitorReadSet) {
				m.read(data);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	private void monitorWrite(Object data) {
		try {
			for (DataMonitorWrite m : monitorWriteSet) {
				m.write(data);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public void addDataMonitorRead(DataMonitorRead m) {
		monitorReadSet.add(m);
	}

	public void addDataMonitorWrite(DataMonitorWrite m) {
		monitorWriteSet.add(m);
	}
}
